package com.hps.demo.consumer;

import com.hps.demo.api.RpcRequest;
import org.springframework.util.CollectionUtils;

import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author hps
 * @Description:
 * @Date Created in 18:54 2019/7/11
 */
public class RpcHandler implements InvocationHandler {

    private Map<String, List<String>>  serviceMap = new ConcurrentHashMap<>();

    public RpcHandler(Map<String, List<String>>  serviceMap) {
        this.serviceMap = serviceMap;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        }
        Object result = null;

        int i = 3;
        while (i > 0) {
            i--;
            try {
                String clazzName = method.getDeclaringClass().getName();
                List<String> serviceAddresses = serviceMap.get(clazzName);
                String address = null;
                if (!CollectionUtils.isEmpty(serviceAddresses)) {
                    Random random = new Random(System.currentTimeMillis());
                    address = serviceAddresses.get(random.nextInt(serviceAddresses.size()));
                } else {
                    System.out.println("remote service not exist");
                }
                System.out.println(address);
                String[] hostAndPort = address.split(":");
                Socket socket = new Socket();
                socket.connect(new InetSocketAddress(hostAndPort[0], Integer.parseInt(hostAndPort[1])));
                OutputStream os = socket.getOutputStream();

                ObjectOutputStream oos = new ObjectOutputStream(os);

                RpcRequest request = new RpcRequest();
                request.setClassName(clazzName);
                request.setMethodName(method.getName());
                request.setParameterTypes(method.getParameterTypes());
                request.setArgs(args);
                oos.writeObject(request);

                InputStream is = socket.getInputStream();
                ObjectInputStream ois = new ObjectInputStream(is);
                result = ois.readObject();
                os.flush();
                os.close();
                ois.close();
                socket.close();
                break;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return result;
    }
}
